package com.example.opengldemo.mbo;

import android.opengl.GLES30;
import android.view.View;

import com.example.opengldemo.util.ShaderBuffer;
import com.example.opengldemo.util.ShaderHandle;
import com.example.opengldemo.util.ShaderUtil;
import com.example.opengldemo.util.ShapeFactory;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;

public class EarthDemo {

    int mProgram;

    String vertexShaderPath = "earth/vertex_dq.sh";
    String fragShaderPath = "earth/fragment_dq.sh";

    int m_U_TextureDayHandle;
    String m_U_TextureDayHandle_NAME = "sTextureDay";
    int m_U_TextureNightHandle;
    String m_U_TextureNightHandle_NAME = "sTextureNight";


    int vCount;

    public EarthDemo(View view) {
        initShaderProgram(view);
        initShaderBuffer();
    }

    private void initShaderBuffer() {
        shaderBuffer = new ShaderBuffer();
        float[] vertexArray = getEarthVertexArray(1f, 10);
        float[] earthgenerateTexCoor = getEarthgenerateTexCoor(10);

        int[] bufferId = new int[3];
        GLES30.glGenBuffers(3, bufferId, 0);
        vertexBufferId = bufferId[0];
        normalBufferId = bufferId[0];
        texcoorsBufferId = bufferId[0];

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
        GLES30.glBufferData(
                GLES30.GL_ARRAY_BUFFER,
                vertexArray.length * 4,
                null,
                GLES30.GL_STATIC_DRAW);
        ByteBuffer buffer = (ByteBuffer) GLES30.glMapBufferRange(
                GLES30.GL_ARRAY_BUFFER,
                0,
                vertexArray.length * 4,
                GLES30.GL_MAP_WRITE_BIT | GLES30.GL_MAP_INVALIDATE_BUFFER_BIT
        );
        if(buffer == null)return ;
        FloatBuffer position = (FloatBuffer)buffer.order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexArray).position(0);
        shaderBuffer.setVertexBuffer(position);

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
        GLES30.glBufferData(
                GLES30.GL_ARRAY_BUFFER,
                vertexArray.length * 4,
                null,
                GLES30.GL_STATIC_DRAW);
         buffer = (ByteBuffer) GLES30.glMapBufferRange(
                GLES30.GL_ARRAY_BUFFER,
                0,
                vertexArray.length * 4,
                GLES30.GL_MAP_WRITE_BIT | GLES30.GL_MAP_INVALIDATE_BUFFER_BIT
        );
        if(buffer == null)return ;
         position = (FloatBuffer)buffer.order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexArray).position(0);
        shaderBuffer.setVertexBuffer(position);

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
        GLES30.glBufferData(
                GLES30.GL_ARRAY_BUFFER,
                vertexArray.length * 4,
                null,
                GLES30.GL_STATIC_DRAW);
         buffer = (ByteBuffer) GLES30.glMapBufferRange(
                GLES30.GL_ARRAY_BUFFER,
                0,
                vertexArray.length * 4,
                GLES30.GL_MAP_WRITE_BIT | GLES30.GL_MAP_INVALIDATE_BUFFER_BIT
        );
        if(buffer == null)return ;
         position = (FloatBuffer)buffer.order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexArray).position(0);
        shaderBuffer.setVertexBuffer(position);
    }

    int vertexBufferId;
    int normalBufferId;
    int texcoorsBufferId;

    private void initShaderProgram(View view) {
        String vertexRes = ShaderUtil.loadFromAssetsFile(vertexShaderPath, view.getResources());
        String fragRes = ShaderUtil.loadFromAssetsFile(fragShaderPath, view.getResources());
        mProgram = ShaderUtil.createProgram(vertexRes, fragRes);
        initHandle(mProgram);

    }

    public ShaderHandle shaderHandle;
    public ShaderBuffer shaderBuffer;

    private void initHandle(int program) {
        this.shaderHandle = new ShaderHandle(program);
        this.m_U_TextureDayHandle = GLES30.glGetUniformLocation(program, m_U_TextureDayHandle_NAME);
        this.m_U_TextureNightHandle = GLES30.glGetUniformLocation(program, m_U_TextureNightHandle_NAME);
    }

    public void drawSelf(int textureDay, int textureNight) {
        shaderHandle.program()
                .bindVertexHandle(shaderBuffer.getVertexBuffer())
                .bindTexCoorsHandle(shaderBuffer.getTexcoorBuffer())
                .bindNormalHandle(shaderBuffer.getNormalBuffer())
                .bindCameraHandle()
                .bindMMaxtrixHandle()
                .bindMVPMaxtrixHandle()
                .bindLightHandle()
                .bindDefaultTextureHandle(textureDay);


        GLES30.glActiveTexture(GLES30.GL_TEXTURE3);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureNight);

        GLES30.glUniform1i(shaderHandle.get_U_textureHandle(), 0);
        GLES30.glUniform1i(m_U_TextureNightHandle, 3);

        shaderHandle.run(GLES30.GL_TRIANGLES, shaderBuffer.vCount);

    }


    public static float[] getEarthgenerateTexCoor(int d) {
        int bw = 360 / d;
        int bh = 180 / d;
        float[] result = new float[bw * bh * 6 * 2];
        float sizew = 1.0f / bw;
        float sizeh = 1.0f / bh;
        int c = 0;
        for (int i = 0; i < bh; i++) {
            for (int j = 0; j < bw; j++) {
                float s = j * sizew;
                float t = i * sizeh;
                result[c++] = s;
                result[c++] = t;  // 左上角
                result[c++] = s;
                result[c++] = t + sizeh; //左下
                result[c++] = s + sizew;
                result[c++] = t;        // 右上
                result[c++] = s + sizew;
                result[c++] = t;   //右上
                result[c++] = s;
                result[c++] = t + sizeh; //左下
                result[c++] = s + sizew;
                result[c++] = t + sizeh; //右下
            }
        }
        return result;
    }

    public static float[] getEarthVertexArray(float r, int angleSpan) {

        ArrayList<Float> alVertix = new ArrayList<Float>();
        //vAngle>-90
        for (float vAngle = 90; vAngle > -90; vAngle = vAngle - angleSpan) {
            for (float hAngle = 360; hAngle > 0; hAngle = hAngle - angleSpan) {
                double xozLength = r * Math.cos(Math.toRadians(vAngle));
                float x1 = (float) (xozLength * Math.cos(Math.toRadians(hAngle)));
                float z1 = (float) (xozLength * Math.sin(Math.toRadians(hAngle)));
                float y1 = (float) (r * Math.sin(Math.toRadians(vAngle)));
                xozLength = r * Math.cos(Math.toRadians(vAngle - angleSpan));
                float x2 = (float) (xozLength * Math.cos(Math.toRadians(hAngle)));
                float z2 = (float) (xozLength * Math.sin(Math.toRadians(hAngle)));
                float y2 = (float) (r * Math.sin(Math.toRadians(vAngle - angleSpan)));
                xozLength = r * Math.cos(Math.toRadians(vAngle - angleSpan));
                float x3 = (float) (xozLength * Math.cos(Math.toRadians(hAngle - angleSpan)));
                float z3 = (float) (xozLength * Math.sin(Math.toRadians(hAngle - angleSpan)));
                float y3 = (float) (r * Math.sin(Math.toRadians(vAngle - angleSpan)));
                xozLength = r * Math.cos(Math.toRadians(vAngle));
                float x4 = (float) (xozLength * Math.cos(Math.toRadians(hAngle - angleSpan)));
                float z4 = (float) (xozLength * Math.sin(Math.toRadians(hAngle - angleSpan)));
                float y4 = (float) (r * Math.sin(Math.toRadians(vAngle)));

                alVertix.add(x1);
                alVertix.add(y1);
                alVertix.add(z1);
                alVertix.add(x2);
                alVertix.add(y2);
                alVertix.add(z2);
                alVertix.add(x4);
                alVertix.add(y4);
                alVertix.add(z4);

                alVertix.add(x4);
                alVertix.add(y4);
                alVertix.add(z4);
                alVertix.add(x2);
                alVertix.add(y2);
                alVertix.add(z2);
                alVertix.add(x3);
                alVertix.add(y3);
                alVertix.add(z3);
            }
        }
        int vCount = alVertix.size() / 3;
        float vertices[] = new float[vCount * 3];
        for (int i = 0; i < alVertix.size(); i++) {
            vertices[i] = alVertix.get(i);
        }
        return vertices;
    }

}
